/*
 * TIDA.h
 *
 *  Created on: 07.11.2011
 *      Author: delgado
 */
// See TIDA Manual for a complete description of the TIDA Data Format

#ifndef TIDA_H_
#define TIDA_H_

#include <string>
using std::string;
#include <vector>
using std::vector;
#include <iostream>
using std::ostream;
#include <cstring>
using std::strncmp;
using std::strlen;



namespace TIDA{


	// Generic Return Type for char[]
	typedef string return_char_t;
	typedef int return_byte_t;
	typedef std::ifstream iFilestream_t;
	typedef std::streamoff offset_t;
	typedef std::streampos filePosition_t;
	typedef std::streamsize streamSize_t;
	#define TIDA_SIGNATURE "TIDA" //File Magic Number
	#define TIDA_SIGNATURE_LENGTH strlen(TIDA_SIGNATURE)
	#define isSame_Cstr(s1,s2) !strncmp( (s1), TIDA_SIGNATURE, TIDA_SIGNATURE_LENGTH)

	// ***************
	// * File Header *
	// ***************
	// A data file begins with a header
	class Header
	{
	public:
		// Sizes for the members in Bytes
		enum {	TIDA_BYTES=4,
				FILEVERSION_BYTES=1,
				RESERVED_BYTES=5,
				TOTAL_BYTES=10};

		// definitions for members types
		typedef char tida_t[TIDA_BYTES];
		typedef unsigned char fileVersion_t;
		typedef char reserved_t[RESERVED_BYTES];

	private:
		// Class has structure padding
		tida_t TIDA;
		fileVersion_t fileVersion;
		reserved_t reserved;

	public:
		Header() : TIDA(), fileVersion(), reserved() {}
		Header(iFilestream_t &f, const offset_t& offset=0);
		Header(const Header& header);
		return_char_t getTIDA() const {return return_char_t(TIDA, TIDA_BYTES);}
		return_byte_t getFileVersion() const {return fileVersion;}
		bool isTIDA() {return !strncmp(TIDA, TIDA_SIGNATURE, TIDA_SIGNATURE_LENGTH);}
		friend ostream& operator<<(ostream& out, const Header& h);
		void info(void) const;
	};

	// *******************
	// * Child Directory *
	// *******************
	class ChildDirectory
		{
		public:
			// Size for the members in Bytes
			enum{NAME_OF_FILE_SEGMENT_BYTES=13,
				POINTER_TO_FILE_SEGMENT_BYTES=4,
				TOTAL_BYTES=17};
			// definitions for members types
			typedef char nameOfFileSegment_t[NAME_OF_FILE_SEGMENT_BYTES];
			typedef long int pointerToFileSegment_t;

		private:
			nameOfFileSegment_t nameOfFileSegment;
			pointerToFileSegment_t pointerToFileSegment;
		public:
			ChildDirectory() : nameOfFileSegment(), pointerToFileSegment() {};
			ChildDirectory(iFilestream_t &f, const offset_t& offset);
			ChildDirectory(const ChildDirectory &d);
			return_char_t getNameOfFileSegment() const {return return_char_t(nameOfFileSegment);}
			pointerToFileSegment_t getPointerToFileSegment() const {return pointerToFileSegment;}
			friend ostream& operator<<(ostream& out, const ChildDirectory& childDirectory);
		};

	typedef vector<ChildDirectory> Children_List_t;

	// ****************
	// * File Segment *
	// ****************
	// The file header is followed by the first 'file segment', called the file's root.........
	class FileSegment
	{
		#define NO_SEGMENT_DESCRIPTION ""
		// Sizes for the members in Bytes
		enum {	SEGMENT_TYPE_BYTES=13,
				VERSION_NUMBER_BYTES=2,
				SEGMENT_NAME_BYTES=13,
				DATA_SIZE_BYTES=4,
				DATA_POINTER_BYTES=4,
				RESERVED_BYTES=4,
				NUMBER_OF_CHILDREN_BYTES=2,
				CHILD_DIR_POINTER_BYTES=4,
				TOTAL_BYTES=46};

		// definitions for members types
		typedef char segmentType_t[SEGMENT_TYPE_BYTES];
		typedef unsigned short int versionNumber_t; // 2 bytes
		typedef char segmentName_t[SEGMENT_NAME_BYTES];
		typedef long int dataSize_t; // 4 bytes
		typedef long int dataPointer_t; // 4 bytes
		typedef char reserved_t[RESERVED_BYTES];
		typedef short int numberOfChildren_t; // 2 bytes
		typedef long int childDirPointer_t; // 4 bytes

	protected:
		segmentType_t segmentType;
		versionNumber_t versionNumber;
		segmentName_t segmentName;
		dataSize_t dataSize;
		dataPointer_t dataPointer;
		reserved_t reserved;
		numberOfChildren_t numberOfChildren;
		childDirPointer_t childDirPointer;
	public:
		FileSegment() : segmentType(), versionNumber(), segmentName(), dataSize(), dataPointer(), reserved(), numberOfChildren(), childDirPointer() {}
		FileSegment(iFilestream_t &f, const offset_t& offset);
		FileSegment(const FileSegment& s);
		return_char_t getSegmentType() const {return return_char_t(segmentType);}
		versionNumber_t getVersionNumber() const {return versionNumber;}
		return_char_t getSegmentName() const {return return_char_t(segmentName);}
		dataSize_t getDataSize() const {return dataSize;}
		dataPointer_t getDataPointer() const {return dataPointer;}
		numberOfChildren_t getNumberOfChildren() const {return numberOfChildren;}
		childDirPointer_t getChildDirPointer() const {return childDirPointer;}
		Children_List_t getChildrenList(iFilestream_t &f) const;
		const string SEGMENT_DESCRIPTION() const {return NO_SEGMENT_DESCRIPTION;}
		friend ostream& operator<<(ostream& out, const FileSegment& fileSegment);
	};

	// **********
	// * Recpar *
	// **********
	//Recording parameters
	//
	class Recpar : public FileSegment
	{
	// File Segment Constants
	#define REC_PAR_DESCRIPTION "REC_PAR"

	enum {	DATA_SIZE_BYTES=48,
			VERSION=1,
			NUMBER_OF_CHILDREN=0,
			CHILD_DIR_POINTER=0	};

	// Sizes for the members in Bytes
	enum {	RESERVED_BYTES=36,
			NUMBER_OF_BLOCKS_BYTES=2,
			NUMBER_OF_CHANNELS_BYTES=2,
			NUMBER_OF_POINTS_PER_BLOCK_BYTES=4,
			FREQUENCY_BYTES=4	};

	// definitions for members types
	typedef char reserved_t[RESERVED_BYTES];
	typedef unsigned short int numberOfBlocks_t; 	// 2 bytes
	typedef unsigned short int numberOfChannels_t; 	// 2 bytes
	typedef long int numberOfPointsPerBlock_t; 		// 4 bytes
	typedef float frequency_t;						// 4 bytes

	private:
		reserved_t reserved;
		numberOfBlocks_t numberOfBlocks;
		numberOfChannels_t numberOfChannels;
		numberOfPointsPerBlock_t numberOfPointsPerBlock;
		frequency_t	frequency;
		const string SEGMENT_DESCRIPTION() const {return REC_PAR_DESCRIPTION;}

	public:
		Recpar() : FileSegment(), reserved(), numberOfBlocks(), numberOfChannels(), numberOfPointsPerBlock(), frequency() {}
		Recpar(iFilestream_t &f, const offset_t& offset);
		numberOfBlocks_t getNumberOfBlocks()  const {return numberOfBlocks;}
		numberOfChannels_t getNumberOfChannels()  const  {return numberOfChannels;}
		numberOfPointsPerBlock_t getNumberOfPointsPerBlock() const {return numberOfPointsPerBlock;}
		frequency_t	getFrequency() const {return frequency;}
		friend ostream& operator<<(ostream& out, const Recpar& recpar);
		void info(void) const;
	};

	// ***********
	// * Scalpar *
	// ***********
	// Scaling parameters
	//
	class Scalpar : public FileSegment
	{
		// File Segment Constants
		#define SCAL_PAR_DESCRIPTION "SCAL_PAR"
		#define DATA_LABEL "DATA"
		#define INTR_LABEL "INTR"
		#define REAL_LABEL "REAL"
		enum {	DATA_SIZE_BYTES=543,
				VERSION=1,
				NUMBER_OF_CHILDREN=0,
				CHILD_DIR_POINTER=0	};

		// Sizes for the members in Bytes
		enum {	TYPE_OF_DATA_BYTES=15,
				TITLE_OF_X_AXIS_BYTES=41,
				RESERVED_BYTES=215,
				X_FACTOR_BYTES=4,
				X_OFFSET_BYTES=4,
				TITLE_OF_Y_AXIS_BYTES=41,
				Y_FACTOR_BYTES=4,
				Y_OFFSET_BYTES=4};

		// definitions for members types
		typedef char typeOfData_t[TYPE_OF_DATA_BYTES];
		typedef char titleOfXAxis_t[TITLE_OF_X_AXIS_BYTES];
		typedef char reserved_t[RESERVED_BYTES];
		typedef float xFactor_t;
		typedef float xOffset_t;
		typedef char titleOfYAxis_t[TITLE_OF_Y_AXIS_BYTES];
		typedef float yFactor_t;
		typedef float yOffset_t;

	private:
		typeOfData_t typeOfData;
		titleOfXAxis_t titleOfXAxis;
		reserved_t reserved_X;
		xFactor_t xFactor;
		xOffset_t xOffset;
		titleOfYAxis_t titleOfYAxis;
		reserved_t reserved_Y;
		yFactor_t yFactor;
		yOffset_t yOffset;
		const string SEGMENT_DESCRIPTION() const {return SCAL_PAR_DESCRIPTION;}

	public:
		Scalpar(): typeOfData(), titleOfXAxis(), reserved_X(), xFactor(), xOffset(), titleOfYAxis(), reserved_Y(), yFactor(), yOffset() {}
		Scalpar(iFilestream_t &f, const offset_t& offset);
		return_char_t getTypeOfData()  const {return static_cast<string>(typeOfData).substr(1);}
		return_char_t getTitleOfXAxis()  const {return static_cast<string>(titleOfXAxis).substr(1);}
		xFactor_t getXFactor()  const {return xFactor;}
		xOffset_t getXOffset()  const {return xOffset;}
		return_char_t getTitleOfYAxis()  const {return static_cast<string>(titleOfYAxis).substr(1);}
		yFactor_t getYFactor()  const {return yFactor;}
		yOffset_t getYOffset()  const {return yOffset;}
		bool isTypeDATA() const {return getTypeOfData()==string(DATA_LABEL);}
		bool isTypeINTR() const {return getTypeOfData()==string(INTR_LABEL);}
		bool isTypeREAL() const {return getTypeOfData()==string(REAL_LABEL);}
		friend ostream& operator<<(ostream& out, const Scalpar& scalpar);
		void info(void) const;
	};
	// ************
	// * Comments *
	// ************
	//  Comments
	//
	class Comments : public FileSegment
	{
		// File Segment Constants
		#define COMMENTS_DESCRIPTION "COMMENTS"
		enum {	DATA_SIZE_BYTES=256,	// Default: 256
				VERSION1=1,
				VERSION2=2,
				NUMBER_OF_CHILDREN=0,
				CHILD_DIR_POINTER=0	};

		// Comments Position (Version 2)
		enum {	CAL_FILE_POS=0, STM_FILE_POS, TIME_OF_RECORDING_POS, STOP_WATCH_POS, ADDITIONAL_COMMENT_POS=12	};
		enum {	MAX_COMMENTS=8};

		// definitions for members types
		typedef char Comment_Version1_t[DATA_SIZE_BYTES];
		typedef string Comment_Version2_t;
		typedef Comment_Version2_t Comment_t; // Comment_t hold the default comment type
		typedef vector<Comment_t> ListOfComments_t;

	private:
		Comment_Version1_t Comment_v1;
		Comment_t calFile;
		Comment_t stmFile;
		Comment_t timeOfRecording;
		Comment_t stopWatch;
		ListOfComments_t defaultComment;
		Comment_t additionalComment;
		ListOfComments_t titleComment;
		const string SEGMENT_DESCRIPTION() const {return COMMENTS_DESCRIPTION;}

	public:
		Comments() : FileSegment(), Comment_v1(), calFile(), stmFile(), timeOfRecording(), stopWatch(), defaultComment(), additionalComment(), titleComment() {}
		Comments(iFilestream_t &f, const offset_t& offset);
		bool isVersion1() const {return versionNumber==VERSION1;}
		bool isVersion2() const {return versionNumber==VERSION2;}
		return_char_t getComment_v1() const {if (isVersion1()) return return_char_t(Comment_v1); else return return_char_t();}
		return_char_t getCalFile() const {if (isVersion2()) return calFile; else return return_char_t();}
		return_char_t getStmFile() const {if (isVersion2()) return calFile; else return return_char_t();}
		return_char_t getTimeOfRecording() const {if (isVersion2()) return timeOfRecording; else return return_char_t();}
		ListOfComments_t getDefaultComments() const {if (isVersion2()) return defaultComment; else return ListOfComments_t();}
		return_char_t getAdditionalComment() const {if (isVersion2()) return additionalComment; else return return_char_t();}
		ListOfComments_t getTitleComments() const {if (isVersion2()) return titleComment; else return ListOfComments_t();}
		friend ostream& operator<<(ostream& out, const Comments& comments);
		void info(void) const;

	};
	// ************
	// * Raw Data *
	// ************
	// Raw Data
	//
	class RawData : public FileSegment
	{
		// File Segment Constants
		#define RAWDATA_DESCRIPTION "RAW_DTA"
		enum {	VERSION=1,
				NUMBER_OF_CHILDREN=0,
				CHILD_DIR_POINTER=0	};


		// definitions for members types
		// for using with DATA or INTR
		typedef short int SmallInt;
		typedef vector<SmallInt> intVec1_t; // 1D
		typedef vector<intVec1_t> intVec2_t; //2D
		typedef vector<intVec2_t> intVec3_t; // 3D
		// for using with REAL
		typedef float SingleFloat;
		typedef vector<SingleFloat> floatVec1_t; // 1D
		typedef vector<floatVec1_t> floatVec2_t; //2D
		typedef vector<floatVec2_t> floatVec3_t; // 3D
		// definitions for indexing
		typedef long unsigned int index_t;


	private:
		intVec3_t intData;
		floatVec3_t floatData;



	public:
		RawData(): FileSegment(), intData(), floatData() {}
		RawData(iFilestream_t &f,const Children_List_t &tiRoot);
		RawData(iFilestream_t &f, const offset_t& offset, const Recpar &recpar, const Scalpar &scalpar);
		const string SEGMENT_DESCRIPTION() const {return RAWDATA_DESCRIPTION;}
		bool isIntData() const {return !intData.empty();}
		bool isFloatData() const {return !floatData.empty();}
		SmallInt getIntPoint(const index_t point, const index_t channel=0, const index_t block=0) const {return intData[channel][block][point];}
		SingleFloat getFloatPoint(const index_t point, const index_t channel=0, const index_t block=0) const {return floatData[channel][block][point];}
		SmallInt operator()(const index_t point, const index_t channel=0, const index_t block=0) const {return intData[channel][block][point];} // Default is INTR

		friend ostream& operator<<(ostream& out, const RawData& rawData);

	};

	// ***********
	// * StmData *
	// ***********
	//
	class StmData : public FileSegment
	{
		// File Segment Constants
		#define STM_DTA_DESCRIPTION "STM_DTA"
		// definitions for members types
		typedef string plain_text_t;

	private:
		plain_text_t commands;
		const string SEGMENT_DESCRIPTION() const {return STM_DTA_DESCRIPTION;}

	public:
		StmData() : FileSegment(), commands() {}
		StmData(iFilestream_t &f, const offset_t& offset);
		return_char_t getCommands() const {return static_cast<return_char_t>(commands);}
		friend ostream& operator<<(ostream& out, const StmData& stmData);
		void info(void) const;

	};

	// ************
	// * AmplPar2 *
	// ************
	//
	class AmplPar2_Base
	{
		// Class Constants
		enum{ 	ARRAY_OF_CHAR_SIZE=8,
				DATA_SIZE=212	};

		// Sizes for the members in Bytes
	public:
		enum{	RANGE_BYTES=4,
				GAIN_BYTES=4,
				VMEM_BYTES=8,
				LJ_BYTES=8,
				V0_BYTES=8,
				CFASTSUM_BYTES=8,
				CFASTPERCENT_BYTES=4,
				CFASTTAU_BYTES=8,
				CSLOW_BYTES=8,
				RSERIES_BYTES=8,
				RSCOMP_BYTES=4,
				RSCOMPTAU_BYTES=4,
				LEAKCOMP_BYTES=8,
				FILTER1INDEX_BYTES=4,
				FILTER2BESSEL_BYTES=4,
				FILTER2FREQ_BYTES=8,
				STIM_BYTES=4,
				EXTSTIMSCAL_BYTES=8,
				BOARDNUMBER_BYTES=2,
				ORIGVERSION_BYTES=2,
				NEWPARASVALID_BYTES=2,
				MUXSOURCE_BYTES=8,
				CSLOWRANGE_BYTES=8,
				MODE_BYTES=8,
				IMON_BYTES=8,
				TITLEIMON_BYTES=8,
				UNITIMON_BYTES=8,
				RMON_BYTES=8,
				TITLEVMEM_BYTES=8,
				UNITVMEM_BYTES=8,
				TITLERMON_BYTES=8,
				UNITRMON_BYTES=8,
				MPCHECK_BYTES=2,
				NOISECHECK_BYTES=2,
				CALCRCHECK_BYTES=2
		};
		// definitions for members types
		// based on Delphi types
		// http://www.delphibasics.co.uk/Article.asp?Name=DataTypes
		typedef unsigned short int SmallInt_t; // 2 bytes
		typedef long int integer_t; // 4 bytes
		typedef double double_t; // 8 bytes
		typedef char arrayOfChar_t[ARRAY_OF_CHAR_SIZE]; // 8-bytes

	public:
		integer_t Range;
		integer_t Gain;
		double_t Vmem;
		double_t LJ;
		double_t V0;
		double_t CFastSum;
		integer_t CFastPercent;
		double_t CFastTau;
		double_t Cslow;
		double_t Rseries;
		integer_t RsComp;
		integer_t RsCompTau;
		double_t LeakComp;
		integer_t Filter1Index;
		integer_t Filter2Bessel;
		double_t Filter2Freq;
		integer_t Stim;
		double_t ExtStimScal;
		SmallInt_t BoardNumber;
		SmallInt_t OrigVersion;
		SmallInt_t NewParasValid;
		arrayOfChar_t MuxSource;
		arrayOfChar_t CSlowRange;
		arrayOfChar_t Mode;
		double_t Imon;
		arrayOfChar_t TitleIMon;
		arrayOfChar_t UnitIMon;
		double_t Rmon;
		arrayOfChar_t TitleVMem;
		arrayOfChar_t UnitVmem;
		arrayOfChar_t TitleRMon;
		arrayOfChar_t UnitRMon;
		SmallInt_t MPCheck;
		SmallInt_t NoiseCheck;
		SmallInt_t CalcRCheck;


	public:
		AmplPar2_Base() {} // Default constructor. No further actions needed
		friend ostream& operator<<(ostream& out, const AmplPar2_Base& a);
		void info(void) const;
	};

	class AmplPar2 : public FileSegment
	{
		// File Segment Constants
		#define AMPLPAR2_DESCRIPTION "AMPLPAR2"
		#define AMPLPAR2_TYPE "AMPLPAR"
		enum {	VERSION=2,
				NUMBER_OF_CHILDREN=0,
				CHILD_DIR_POINTER=0,
				AMPLPAR_BASE_SIZE=212	};
		// definitions for member types
		typedef vector<AmplPar2_Base> AmplPar2_t;
		typedef unsigned char numberOfAmplifier_t; //from 1 to 3

	private:
		AmplPar2_t parameters;


	public:
		AmplPar2(): FileSegment(), parameters() {}
		AmplPar2(iFilestream_t &f, const offset_t& offset);
		const string SEGMENT_DESCRIPTION() const {return AMPLPAR2_DESCRIPTION;}
		const string SEGMENT_TYPE() const {return AMPLPAR2_TYPE;}
		numberOfAmplifier_t getNumberOfAmplifiers() const {return parameters.size();}
		AmplPar2_Base getAmplifierParameters(const numberOfAmplifier_t& amplifierNumber) const {return parameters[amplifierNumber];}
		AmplPar2_Base operator[](const numberOfAmplifier_t& amplifierNumber) const {return parameters[amplifierNumber];}
		friend ostream& operator<<(ostream& out, const AmplPar2& amplpar2);
		void info(const int amplifier=0) const;
	};

	// ************
	// * TIDAfile *
	// ************
	//
	class TIDAFile
	{
			// Positions of the segments in tiRootChildren
			enum {	RECPAR_POS=0,
					SCALPAR_POS=1,
					COMMENTS_POS=2,
					RAWDATA_POS=3,
					STMDTA_POS=4,
					AMPLPAR2_POS=5,
					NUMBER_OF_CHILDREN=7
				};
	private:
		// Main Members (Stores Processed Information)
		string filename;

		// Accessory Members (Stores Raw Information from TIDA File)
//		File file;
		streamSize_t sizeOfFile;
		Header header;
		FileSegment rootWholeFile;
		Children_List_t rootWholeFileChildren;
		FileSegment tiRoot;
		Children_List_t tiRootChildren;
		Recpar recpar;
		Scalpar scalpar;
		Comments comments;
		RawData rawdata;
		StmData stmData;
		AmplPar2 amplpar2;

	public:
		TIDAFile();
		TIDAFile(const string& filename, const bool verbose=false);
		Header getHeader() const {return header;}
		FileSegment getRootWholeFile() const {return rootWholeFile;}
		Children_List_t getRootWholeFileChildren() const {return rootWholeFileChildren;}
		FileSegment getTiRoot() const {return tiRoot;}
		Children_List_t getTiRootChildren() const {return tiRootChildren;}
		Recpar getRecpar() const {return recpar;}
		Scalpar getScalpar() const {return scalpar;}
		Comments getComments() const {return comments;}
		RawData getRawdata() const {return rawdata;}
		StmData getStmData() const {return stmData;}
		// Returns Y Scaled Value
		float getYScaled(const long int point, const long int block=0, const long int channel=0) const {return scalpar.getYFactor()*rawdata(point, channel, block);}
		float operator()(const long int point, const long int block=0, const long int channel=0) const {return scalpar.getYFactor()*rawdata(point, channel, block);}		// Returns X Scaled Value
		float getXScaled(const long int point) const {return scalpar.getXFactor()*point;}
		float operator[](const long int point) const {return scalpar.getXFactor()*point;}
		// TIDA to ASCII
		bool toASCII(const string destinationFilename="",  const bool overwrite=false, const bool verbose=false, const string separator="\t", const string newline="\r\n");
		// view
		void viewFileStructure(void) const;
		void info(void) const;
		void headerInfo() const {return header.info();}
		void recparInfo() const {return recpar.info();}
		void scalparInfo() const {return scalpar.info();}
		void commentsInfo() const {return comments.info();}
		void amplparInfo(const int amplifier=0) const {return amplpar2.info(amplifier);}

		friend ostream& operator<<(ostream& out, const ChildDirectory& childDirectory);
	};







} // end namespace TIDA

#endif
